// SimpleRegAllocator.cpp 
// Implement class SimpleRegAllocator
#include "crossbit/SimpleRegAllocator.h"
#include "crossbit/VBlock.h"
#include "crossbit/VInst.h"

using namespace crossbit;

void SimpleRegAllocator::init(VBlock *vb)
{
    ra_free_regs.clear();
				vinst_seq = 0;
    cal_treg=0;
				struct RegUsage ru;
				ru.status = RegAllocator::FREE;
				ru.mapped_to = -1;
				ru.inst = 0;
				for (XTRegNum i = 0; i < ra_num_of_reg; ++i)
				{
        ra_free_regs.insert(i);
								treg_table[i]=ru;
				}
			 UTIL::XTMemset(vreg_spilledIn,0,sizeof(vreg_spilledIn));
}

void SimpleRegAllocator::phaseTask()
{
}

// Allocate target register for virtual register
XTRegNum SimpleRegAllocator::regAlloc(XTRegNum vreg, RegAccessMode mode)
{
   //printf("This is regAlloc calling!\n");
				XTRegNum alloc;
				for(XTRegNum i=0;i<ra_num_of_reg;i++)
									{
											if(treg_table[i].status==ALLOCATED && treg_table[i].mapped_to==vreg)
										{
            if(vreg_spilledIn[vreg]==false&&mode==USE)
            {
              regSpillIn(vreg,i);
              vreg_spilledIn[vreg]=true;
            }
												  treg_table[i].inst=vinst_seq;
             // printf("1 %d vreg alloc %d treg\n",vreg,i);
														return i;
									 }
									} 
				//for(XTRegNum i=0;i<ra_num_of_reg;i++)
				if(ra_free_regs.empty()==false)
								{  
          alloc=*(ra_free_regs.begin());
          ra_free_regs.erase(ra_free_regs.begin());
									//	if(treg_table[i].status==FREE)
          // {
            if(vreg_spilledIn[vreg]==false&&mode==USE)
           { 
            regSpillIn(vreg,alloc);
											 vreg_spilledIn[vreg]=true;
            }
												treg_table[alloc].status=ALLOCATED;
												treg_table[alloc].mapped_to=vreg;
												treg_table[alloc].inst=vinst_seq;
         //   printf("2 %d vreg alloc %d treg\n",vreg,i);
												return alloc;
										//	}
								} 
	
           // cal_treg=7;
           // find ALLOCATED target register whose mapped virtual regiser 
												// using vreg%8 to allocate the target regs.
												for(XTInt32 cn=0;cn<8;cn++)
												{
												struct RegUsage tru=treg_table[cal_treg];
											if (tru.status!=RegAllocator::ALLOCATED||tru.inst==vinst_seq)//RESERVED register can't be used&&The treg who is using in the current instruction.
														cal_treg=(cal_treg+1)%8;
											  else
                {
												     break;
                }
												}
												alloc=cal_treg;
            cal_treg=(cal_treg+1)%8;
												regSpillOut(alloc);
 			if (mode==USE)
   {
		 	regSpillIn(vreg, alloc);
				vreg_spilledIn[vreg]=true;
   }
				treg_table[alloc].status = ALLOCATED;
				treg_table[alloc].mapped_to = vreg;
				treg_table[alloc].inst = vinst_seq; 
  //  printf("3 %d vreg alloc %d treg\n",vreg,alloc);
    return alloc;
  
}

// Force allocate target register "expect" to "vreg"
//
// Algorithms:
//  if (vreg is already allocated to expect)
//	return immediately
//  else 
//	empty "expect" register for "Vreg";
//	if (vreg is currently allocated to another target register)
//	    move it to "expect"
//	if (vreg is spilled)
//	    load vreg from spill pool to expect
//	...
// 
XTRegNum SimpleRegAllocator::regAllocForce(XTRegNum vreg, XTRegNum expect, RegAccessMode mode)
{
//    printf("This is regAllocForce calling!\n");
    RegUsage etru=treg_table[expect];
				assert(etru.status != RegAllocator::RESERVED); // dead-lock
			 if (etru.status==RegAllocator::FREE)
           ra_free_regs.erase(expect); 	
			  if (etru.status == RegAllocator::ALLOCATED) 
								{
            if(etru.mapped_to==vreg)
           {
            treg_table[expect].inst=vinst_seq;
            if(vreg_spilledIn[vreg]==false&&mode==USE)
           {
            regSpillIn(vreg,expect);
            vreg_spilledIn[vreg]=true;
           }
          //  printf("******1 %d vreg alloc %d treg\n",vreg,expect);
            return expect;
           }
            regSpillOut(expect);
        }
            for(XTRegNum i=0;i<ra_num_of_reg;i++)
												{
														if(treg_table[i].status==ALLOCATED&&treg_table[i].mapped_to==vreg)
             {
                (*ra_cb_reg_to_reg)(i,expect);
                ra_free_regs.insert(i);
                treg_table[expect].status=ALLOCATED;
                treg_table[expect].inst=vinst_seq;
																treg_table[expect].mapped_to=vreg;
                treg_table[i].status=FREE;
           // printf("******2 %d vreg alloc %d treg\n",vreg,expect);
                return expect;
												}
							   	}
        vreg_spilledIn[vreg]=false;
								if (mode==USE) 
											{
            	regSpillIn(vreg, expect);
             vreg_spilledIn[vreg]=true;
           }

							 treg_table[expect].status=ALLOCATED;
								treg_table[expect].mapped_to=vreg;
        treg_table[expect].inst=vinst_seq;
      //  printf("******3 %d vreg alloc %d treg\n",vreg,expect);
				return expect;
}

// Force "vreg" NOT to be allocated to "except"
//
// Algorithm:
//	if vreg is already allocated and it's not "except"
//	    return immediately
//	else 
//	    "reserve" "except" register
//	    alloc = regAlloc(vreg, mode)
//	    "release" "except" register
//
XTRegNum SimpleRegAllocator::regAllocForceExcept(XTRegNum vreg, XTRegNum except, RegAccessMode mode)
{
  //  printf("This is regAllocForceExcept calling!\n");
    XTInt32 temp=treg_table[except].status;
    XTRegNum alloc;
				if (temp== ALLOCATED && treg_table[except].mapped_to == vreg)
			{				
     regSpillOut(except);
     temp=FREE;
   }
     else
    {
     if(temp==FREE)
     ra_free_regs.erase(except);
    }
     treg_table[except].status=RESERVED;
     alloc=regAlloc(vreg,mode);
     treg_table[except].status=temp;
     if (temp==FREE)
     ra_free_regs.insert(except);
		 		return alloc;
}

void SimpleRegAllocator::regAllocReserve(XTRegNum treg)
{
    //printf("This is regAllocReserve calling!\n");
				struct RegUsage &tru = treg_table[treg];
				assert(tru.status != RegAllocator::RESERVED && "regAllocReserve"); 
			 if(tru.inst==vinst_seq) printf("Can't reserve the reg here!!\n");
    if(tru.status==RegAllocator::FREE)
            ra_free_regs.erase(treg);
   	if (tru.status == RegAllocator::ALLOCATED)
								regSpillOut(treg);
				tru.status = RegAllocator::RESERVED;
    tru.mapped_to = 0;
				return;
}


void SimpleRegAllocator::regAllocRelease(XTRegNum treg)// Release only affect the RESERVED Registers.
{
				struct RegUsage &tru = treg_table[treg];
				assert(tru.status == RESERVED);
    ra_free_regs.insert(treg);
				tru.status = RegAllocator::FREE;
				return;
}

void SimpleRegAllocator::regSpillIn(XTRegNum vreg, XTRegNum treg)
{
		(*ra_cb_spill_in)((XTMemAddr)(ra_spill_pool+vreg),treg); 
}
XTRegNum SimpleRegAllocator::regSpillOut(XTRegNum treg)
{
				assert(treg_table[treg].status == RegAllocator::ALLOCATED);	

				XTRegNum vreg = treg_table[treg].mapped_to;

				(*ra_cb_spill_out)(treg, (XTMemAddr)(ra_spill_pool+vreg));
    vreg_spilledIn[vreg]=false;
				return treg;
}
